﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SeaBattle.Domain.Abstract;
using SeaBattle.Domain.Abstract.Services;

namespace SeaBattle.Domain.Entities
{
    public class Game
    {
        public int Id { get; private set; }
        public Player Creator { get; private set; }
        public DateTime Created { get; private set; }
        private Player opponent;
        public int Preset { get; set; }
        public GameState State { get; private set; }
        public Player Opponent { get{return opponent;} set{SetOpponent(value);} }
        public Dictionary<Player, Fleet> Navies { get; set; }

        private IFleetRepository fleetRepository;
        private IShipRepository shipRepository;
        private IPlayerRepository playerRepository;

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="Game"/> is opened.
        /// </summary>
        /// <value>
        ///   <c>true</c> if opened; otherwise, <c>false</c>.
        /// </value>
        public bool Opened { 
            get
            {
                return State == GameState.STATE_OPENED;
            } 
            set
            {
                if (value)
                    State = GameState.STATE_OPENED ;
            } 
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="Game"/> is closed.
        /// </summary>
        /// <value>
        ///   <c>true</c> if closed; otherwise, <c>false</c>.
        /// </value>
        public bool Closed
        {
            get { return State == GameState.STATE_CLOSED; }
            set
            {
                if (value)
                {
                    State = GameState.STATE_CLOSED;
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [board preparing].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [board preparing]; otherwise, <c>false</c>.
        /// </value>
        public bool BoardPreparing
        {
            get { return State == GameState.STATE_BOARD_SETUP; }
            set
            {
                if (value)
                {
                    State = GameState.STATE_BOARD_SETUP;
                    Navies = new Dictionary<Player, Fleet>();
                    InitializeFleet(Creator);
                    InitializeFleet(Opponent);
                }
            }
        }


        /// <summary>
        /// Initializes a new instance of the <see cref="Game"/> class.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="created">The created.</param>
        /// <param name="creator">The creator.</param>
        /// <param name="shipRepository">The ship repository.</param>
        /// <param name="playerService">The player service.</param>
        /// <param name="fleetRepository">The fleet repository.</param>
        public Game(int id, DateTime created, Player creator, IShipRepository shipRepository, IFleetRepository fleetRepository, IPlayerRepository playerRepository)
        {
            Id = id;
            Created = created;
            Creator = creator;
            State = GameState.STATE_OPENED;
            this.shipRepository = shipRepository;
            this.playerRepository = playerRepository;
            this.fleetRepository = fleetRepository;
              
        }

        /// <summary>
        /// Initializes the fleet.
        /// </summary>
        /// <param name="player">The player.</param>
        private void InitializeFleet(Player player)
        {
            Fleet fleet = fleetRepository.CreateFleet(0, 0);
            Navies.Add(player,fleet);
        }

        /// <summary>
        /// Sets the opponent.
        /// </summary>
        /// <param name="theOpponent">The opponent.</param>
        private void SetOpponent(Player theOpponent)
        {
            if (!Opened)
            {
                throw new Exception("The game " + Id + " not opened");
            }

            if (Creator == theOpponent)
            {
                throw new Exception("The opponent is the same as creator of the game " + Id);
            }

            opponent = theOpponent;
        }

        /// <summary>
        /// Joins the opponent.
        /// </summary>
        /// <param name="theOpponent">The opponent.</param>
        public void JoinOpponent(Player theOpponent)
        {
            Opponent = theOpponent;
            BoardPreparing = true;
        }

        /// <summary>
        /// Adds the ship.
        /// </summary>
        /// <param name="player">The player.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="partsCount">The parts count.</param>
        /// <param name="orientation">The orientation.</param>
        /// <returns></returns>
        public Ship AddShip(Player player, int x, int y, byte partsCount, ShipOrientation orientation)
        {
            if (!BoardPreparing)
            {
                throw new Exception("The game is not in valid (Board Preparing) state");  
            }
            
            
            if (!Navies.ContainsKey(player))
            {
                throw new Exception("Player doesn't exist");  
            }

            Ship ship = shipRepository.CreateShip(x,y,partsCount);
            Navies[player].AddShip(ship,orientation);
            return ship;
        }

        /// <summary>
        /// Removes the ship.
        /// </summary>
        /// <param name="id">The id.</param>
        public void RemoveShip(int id)
        {

            if (!BoardPreparing)
            {
                throw new Exception("The game is not in valid (Board Preparing) state");
            }
            
            Ship ship = shipRepository.GetShip(id);
            
            if (ship == null)
            {
                throw new Exception("There is no ship with id = "+ id.ToString());  
            }


            // In case Creator owns the ship
            Navies[Creator].RemoveShip(ship);

            // In case Opponent owns the ship
            Navies[Opponent].RemoveShip(ship);

            // Remove ship from ShipRepository
            shipRepository.RemoveShip(ship);
                  
        }

        /// <summary>
        /// Resets the fleet.
        /// </summary>
        /// <param name="playerId">The player id.</param>
        public void ResetFleet(int playerId)
        {
            if (!BoardPreparing)
            {
                throw new Exception("The game is not in valid (Board Preparing) state");
            }
            
            
            Player player = playerRepository.GetPlayer(playerId);

            if (Creator != player && Opponent != player)
            {
                throw new Exception("Player with id = "+playerId.ToString()+" does not belong to this game");
            }

            Navies[player].ResetFleet();
        }


    }

    public enum GameState
    {
        STATE_CREATED = 0,
        STATE_GAME_SETUP,
        STATE_OPENED,
        STATE_BOARD_SETUP,
        STATE_PLAY,
        STATE_PAUSE,
        STATE_CLOSED
    }
}
